/*
 * Copyright (C) 2010-2011 Enrique Lara (enrique.posix2.lara@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package trs.wmata;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;

import android.net.Uri;

import app.timetogo.R;

import app.timetogo.model.Agency;
import app.timetogo.model.Alarm;
import app.timetogo.model.App;
import app.timetogo.model.Route;
import app.timetogo.model.Stop;
import app.timetogo.model.Vehicle;

import app.timetogo.provider.Tables.Alarms;

import trs.wmata.api.Api;
import trs.wmata.api.NextTrain;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Properties;
import java.util.TreeSet;

import org.xml.sax.SAXException;

public final class TrainAgency implements Agency {
    public static final int WMATA_TRAIN_AGENCY_ID = 100;
    private static final String NAME = "METRO";
    private static final String URL = "http://wwww.wmata.com";
    private static final String TIME_ZONE = "America/New_York";
    private static final HashMap<String, TreeSet<TrainStation>> LINE_TO_STATION_MAP = new HashMap<String, TreeSet<TrainStation>>();
    private final Context context;
    private final Api api;
    private static String[] TRAIN_STATION_NAMES = null;
    private static String[] TRAIN_ROUTE_NAMES = null;
    private static final String[] TRAIN_ROUTE_CODES = {
                                                          TrainLine.BLUE.getCode(),
                                                          TrainLine.GREEN.getCode(),
                                                          TrainLine.ORANGE.getCode(),
                                                          TrainLine.RED.getCode(),
                                                          TrainLine.YELLOW.getCode()
    };

    public static TrainAgency getInstance(final Context context) {
        return new TrainAgency(context);
    }

    TrainAgency(final Context context) {
        this.context = context;

        Properties config = App.getConfig(context);
        api = new Api(this, config.getProperty("wmata.api.baseUrl"), config.getProperty("wmata.api.key"));
    }

    private static synchronized void initTrainStations() {
        TRAIN_STATION_NAMES = new String[TrainStation.values().length];

        final TrainStationComparator comparator = new TrainStationComparator();
        int i = 0;

        LINE_TO_STATION_MAP.clear();

        for (final TrainStation station : TrainStation.values()) {
            for (final String line : station.getRouteCodes()) {
                if (!LINE_TO_STATION_MAP.containsKey(line)) {
                    LINE_TO_STATION_MAP.put(line, new TreeSet<TrainStation>(comparator));
                }

                LINE_TO_STATION_MAP.get(line).add(station);
            }

            TRAIN_STATION_NAMES[i++] = station.getName();
        }
    }

    public ArrayList<Vehicle> getVehicles(final Stop station) throws IOException, SAXException {
        return getVehicles(station.getCode());
    }

    //XXXX rename getRoutes(stopName);
    public ArrayList<Vehicle> getVehicles(final String apiCode) throws IOException, SAXException {
        final TrainStation station = getStationByCode(apiCode);

        if (station == null) {
            return new ArrayList<Vehicle>(0);
        }

        final String codeStr = station.getCode();
        final String altCode = station.getAltCode();

        final NextTrain stationPrediction = api.getNextTrain(codeStr, altCode);

        final ArrayList<Train> trains = stationPrediction.getTrains();
        final ArrayList<Vehicle> rval = new ArrayList<Vehicle>(trains.size());
        rval.addAll(trains);

        return rval;
    }

    public Stop getStopByName(final String name) {
        for (final Stop station : TrainStation.values()) {
            if (station.getName().equalsIgnoreCase(name)) {
                return station;
            }
        }

        return TrainStation.UNKNOWN;
    }

    public Stop getStopByCode(final String apiCode) {
        return getStationByCode(apiCode);
    }

    private TrainStation getStationByCode(final String apiCode) {
        for (final TrainStation station : TrainStation.values()) {
            if (equalsIgnoreCase(station.getCode(), apiCode) || equalsIgnoreCase(station.getAltCode(), apiCode)) {
                return station;
            }
        }

        return TrainStation.UNKNOWN;
    }

    public Route getRouteByCode(final String routeCode) {
        return getTrainLineByCode(routeCode);
    }

    private TrainLine getTrainLineByCode(final String routeCode) {
        for (final TrainLine line : TrainLine.values()) {
            if (equalsIgnoreCase(line.getCode(), routeCode)) {
                return line;
            }
        }

        return TrainLine.UNKNOWN;
    }

    private static boolean equalsIgnoreCase(final String s1, final String s2) {
        return (s1 != null) ? s1.equalsIgnoreCase(s2) : (s2 == null);
    }

    public String[] getRouteNames() {
        if (TRAIN_ROUTE_NAMES == null) {
            initTrainRoutes(context);
        }

        return TRAIN_ROUTE_NAMES;
    }

    public String[] getRouteCodes() {
        return TRAIN_ROUTE_CODES;
    }

    private static synchronized void initTrainRoutes(final Context context) {
        TRAIN_ROUTE_NAMES = new String[5];
        TRAIN_ROUTE_NAMES[0] = context.getString(TrainLine.BLUE.getNameId());
        TRAIN_ROUTE_NAMES[1] = context.getString(TrainLine.GREEN.getNameId());
        TRAIN_ROUTE_NAMES[2] = context.getString(TrainLine.ORANGE.getNameId());
        TRAIN_ROUTE_NAMES[3] = context.getString(TrainLine.RED.getNameId());
        TRAIN_ROUTE_NAMES[4] = context.getString(TrainLine.YELLOW.getNameId());
    }

    // XXX move to Route. getStopNames();
    public String[] getStopNames(final String line) {
        final TreeSet<TrainStation> stations = LINE_TO_STATION_MAP.get(line);

        if (stations == null) {
            return getAllStationNames();
        }

        final String[] rval = new String[stations.size()];

        int i = 0;

        for (final Stop station : stations) {
            rval[i++] = station.getName();
        }

        return rval;
    }

    public String[] getStopCodes(final String line) {
        final TreeSet<TrainStation> stations = LINE_TO_STATION_MAP.get(line);

        if (stations == null) {
            return getAllStationNames();
        }

        final String[] rval = new String[stations.size()];

        int i = 0;

        for (final Stop station : stations) {
            rval[i++] = station.getCode();
        }

        return rval;
    }

    private String[] getAllStationNames() {
        if (TRAIN_STATION_NAMES == null) {
            initTrainStations();
        }

        return TRAIN_STATION_NAMES;
    }

    //XXX seems to assume a rail station, rename to use "stop"
    public boolean gotoViewStop(final String apiCode) {
        final Stop station = this.getStopByCode(apiCode);

        //<action android:name="android.intent.action.VIEW" />
        //<category android:name="android.intent.category.DEFAULT" />
        //<data android:mimeType="application/wmata-nexttrain" />
        if (station == null) {
            return false;
        }

        final Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.putExtra(Alarms.AGENCY_ID, WMATA_TRAIN_AGENCY_ID); //XXX may be redundant with type
        intent.putExtra(Alarms.STOP, station.getCode());
        intent.setType("application/wmata-nexttrain");
        context.startActivity(intent);

        return true;
    }

    public String getName() {
        return NAME;
    }

    public String getUrl() {
        return URL;
    }

    public String getTimeZone() {
        return TIME_ZONE;
    }

    public int getPreferenceId() {
        return R.xml.alarm_pref;
    }

    @Override
    public Alarm newAlarm(final Uri uri) {
        final ContentValues values = new ContentValues();

        final Calendar c = Calendar.getInstance();

        values.put(Alarms.AGENCY_ID, WMATA_TRAIN_AGENCY_ID);
        values.put(Alarms.ENABLED, 0);
        values.put(Alarms.HOUR, c.get(Calendar.HOUR_OF_DAY));
        values.put(Alarms.MINUTE, c.get(Calendar.MINUTE));
        values.put(Alarms.DAYS_IN_WEEK, 0);
        values.put(Alarms.STOP, TrainStation.L_ENFANT_PLAZA.getCode());
        values.put(Alarms.ROUTE, TrainLine.BLUE.getCode());
        values.put(Alarms.DIRECTION, TrainStation.FRANCONIA_SPRINGFIELD.getCode());
        values.put(Alarms.TIME_MAX, 17);
        values.put(Alarms.TIME_MIN, 7);
        values.put(Alarms.VIBRATE, 1);
        values.put(Alarms.IMAGE, R.drawable.time_to_transit);

        final Uri alarmUri = context.getContentResolver().insert(uri, values);

        if (alarmUri == null) {
            return null;
        }

        return App.loadAlarm(alarmUri, context);
    }
}
